﻿/*
    PureText+ - http://code.google.com/p/puretext-plus/
    
    Copyright (C) 2003 Steve P. Miller, http://www.stevemiller.net/puretext/
    Copyright (C) 2011 Melloware, http://www.melloware.com
    
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
    
    The idea of the Original PureText Code is Copyright (C) 2003 Steve P. Miller
    
    NO code was taken from the original project this was rewritten from scratch
    from just the idea of Puretext.
*/
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace PureTextPlus
{
	/// <summary>
	/// Hotkey keyboard hook for monitoring Windows+V or whatever key combination chosen.
	/// Based on Blog Post: http://www.liensberger.it/web/blog/?p=207
	/// </summary>
	public sealed class HotkeyHook : IDisposable
	{
		// Registers a hot key with Windows.
		[DllImport("user32.dll")]
		private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
		// Unregisters the hot key with Windows.
		[DllImport("user32.dll")]
		private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
		
		// instance variable to hold the native window
		private Window _window = new Window();
		private int _currentId;

		/// <summary>
		/// Represents the window that is used internally to get the messages.
		/// </summary>
		private class Window : NativeWindow, IDisposable
		{
			private static int WM_HOTKEY = 0x0312;

			public Window()
			{
				// create the handle for the window.
				this.CreateHandle(new CreateParams());
			}

			/// <summary>
			/// Overridden to get the notifications.
			/// </summary>
			/// <param name="m"></param>
			protected override void WndProc(ref Message m)
			{
				base.WndProc(ref m);

				// check if we got a hot key pressed.
				if (m.Msg == WM_HOTKEY)
				{
					// get the keys.
					Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);
					ModifierKeys modifier = (ModifierKeys)((int)m.LParam & 0xFFFF);

					// invoke the event to notify the parent.
					if (KeyPressed != null)
						KeyPressed(this, new KeyPressedEventArgs(modifier, key));
				}
			}

			public event EventHandler<KeyPressedEventArgs> KeyPressed;

			#region IDisposable Members

			public void Dispose()
			{
				this.DestroyHandle();
			}

			#endregion
		}

		public HotkeyHook()
		{
			// register the event of the inner native window.
			_window.KeyPressed += delegate(object sender, KeyPressedEventArgs args)
			{
				if (KeyPressed != null)
					KeyPressed(this, args);
			};
		}

		/// <summary>
		/// Registers a hot key in the system.
		/// </summary>
		/// <param name="modifier">The modifiers that are associated with the hot key.</param>
		/// <param name="key">The key itself that is associated with the hot key.</param>
		public void RegisterHotKey(ModifierKeys modifier, Keys key)
		{
			// increment the counter.
			_currentId = _currentId + 1;

			// register the hot key.
			if (!RegisterHotKey(_window.Handle, _currentId, (uint)modifier, (uint)key))
				throw new InvalidOperationException("Couldn’t register the hot key.");
		}
		
		/// <summary>
		/// Unregisters any registered hotkeys
		/// </summary>
		public void UnregisterHotKeys() {
			// unregister all the registered hot keys.
			for (int i = _currentId; i > 0; i--)
			{
				UnregisterHotKey(_window.Handle, i);
			}
		}

		/// <summary>
		/// A hot key has been pressed.
		/// </summary>
		public event EventHandler<KeyPressedEventArgs> KeyPressed;

		#region IDisposable Members

		public void Dispose()
		{
			// unregister all the registered hot keys.
			for (int i = _currentId; i > 0; i--)
			{
				UnregisterHotKey(_window.Handle, i);
			}

			// dispose the inner native window.
			_window.Dispose();
		}

		#endregion
	}

	/// <summary>
	/// Event Args for the event that is fired after the hot key has been pressed.
	/// </summary>
	public class KeyPressedEventArgs : EventArgs
	{
		private ModifierKeys _modifier;
		private Keys _key;

		internal KeyPressedEventArgs(ModifierKeys modifier, Keys key)
		{
			_modifier = modifier;
			_key = key;
		}

		public ModifierKeys Modifier
		{
			get { return _modifier; }
		}

		public Keys Key
		{
			get { return _key; }
		}
	}

	/// <summary>
	/// The enumeration of possible modifiers.
	/// </summary>
	[Flags]
	public enum ModifierKeys : uint
	{
		None = 0,
		Alt = 1,
		Control = 2,
		Shift = 4,
		Win = 8
	}

}
